Skip to content

Copy production image with direct Docker registry auth#758

Merged
justin808 merged 1 commit into
masterfrom
jg-codex/use-direct-docker-copy-for-production
Jun 2, 2026
Merged

Copy production image with direct Docker registry auth#758
justin808 merged 1 commit into
masterfrom
jg-codex/use-direct-docker-copy-for-production

Conversation

@justin808

@justin808 justin808 commented Jun 2, 2026

Copy link
Copy Markdown
Member

Summary

  • replace the source-side cpln docker-login/cpln image copy path with direct Docker registry auth
  • authenticate to staging and production registries via docker login --password-stdin
  • copy by docker pull, docker tag, and docker push, while preserving the generated production image tag output

Why

The post-#757 production run got past the previous profile-create issue, but failed because cpln image docker-login still configured Docker with the default production profile for the staging registry.

Then Docker could not pull the staging image. Control Plane image docs document the CI-safe direct registry path using docker login with the token on stdin. This keeps staging and production registry credentials explicit and avoids the cpln profile/credential-helper mismatch.

Failed run: https://github.com/shakacode/react-webpack-rails-tutorial/actions/runs/26849578678

Verification

  • git diff --check -- .github/workflows/cpflow-promote-staging-to-production.yml
  • bin/conductor-exec bin/test-cpflow-github-flow

Note

Medium Risk
Changes how production container images are published during promotion; a misconfiguration could block releases or push to the wrong registry, though tagging and retries are preserved.

Overview
The Copy image from staging step in cpflow-promote-staging-to-production.yml no longer uses cpln image docker-login and cpln image copy. It now logs into the staging and production Control Plane registries with docker login --password-stdin (staging and production tokens via env), then pulls the staging image, tags it with the same incremented production name (<app>:<n+1>_<commit>), and pushes to production.

That fixes CI failures where cpln image docker-login wired Docker to the wrong org/profile so the staging image could not be pulled. Retry logic, isolated DOCKER_CONFIG, and the step’s image output are unchanged.

Reviewed by Cursor Bugbot for commit ab4b524. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • Chores
    • Updated the production deployment workflow to streamline staging-to-production image promotion with improved registry-based image handling mechanisms.

@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown

🚀 Quick Review App Commands

Welcome! Here are the commands you can use in this PR:
They require the repository to have cpflow review apps configured, including the CPLN_TOKEN_STAGING secret.

+review-app-deploy

Deploy your PR branch for testing.

+review-app-delete

Remove the review app when done.

+review-app-help

Show detailed instructions, environment setup, and configuration options.

Comment +review-app-help for full setup details.

@coderabbitai

coderabbitai Bot commented Jun 2, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 651e011d-2f28-4752-b067-fb9d477e7321

📥 Commits

Reviewing files that changed from the base of the PR and between 633cf37 and ab4b524.

📒 Files selected for processing (1)
  • .github/workflows/cpflow-promote-staging-to-production.yml

Walkthrough

The production promotion workflow's image-copy step is refactored to authenticate and transfer images via direct Docker registry operations. Production credentials are added to the environment, staging and production registry endpoints are computed, and the prior cpln-based copy is replaced with a chained flow of docker login, manifest inspection, pull, tag, and push commands.

Changes

Production Image Promotion

Layer / File(s) Summary
Environment and registry configuration
.github/workflows/cpflow-promote-staging-to-production.yml
CPLN_TOKEN_PRODUCTION is added to the step environment to enable production Docker registry authentication. Staging and production registry endpoints are computed from org identifiers, and full source/target image references are constructed for the registry-based transfer.
Docker registry-based image transfer
.github/workflows/cpflow-promote-staging-to-production.yml
The image copy logic is replaced with a chained sequence: docker login for staging and production, docker manifest inspect to validate the source image, docker pull from staging, docker tag to the production reference, and docker push to the production registry.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

Poem

🐰 A workflow so swift, from staging we spring,
With tokens and registries, Docker commands sing,
Pull, tag, and push—the image takes flight,
From dev to production, all done in the night! 🚀

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and accurately summarizes the main change: replacing cpln-based image copy with direct Docker registry authentication for production image promotion.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jg-codex/use-direct-docker-copy-for-production

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@claude

claude Bot commented Jun 2, 2026

Copy link
Copy Markdown

Code Review: Copy production image with direct Docker registry auth

Overview

This PR replaces cpln image docker-login + cpln image copy with explicit docker login --password-stdin / docker pull / docker tag / docker push calls to fix CI failures caused by the cpln credential helper wiring Docker to the wrong org/profile.


Positives

  • Security: printf '%s' "${CPLN_TOKEN_*}" | docker login ... --password-stdin correctly avoids token exposure in /proc/<pid>/cmdline. Consistent with the existing comment rationale.
  • Credential isolation: DOCKER_CONFIG export + mktemp -d + trap ... EXIT cleanup is preserved and still applies to all new docker calls.
  • Correctness: The production_image_ref variable cleanly mirrors how source_image_ref is constructed, reducing copy-paste risk.
  • Error handling: set -euo pipefail and the retry loop with status tracking are unchanged.

Issues / Risks

Medium – Multi-arch / manifest-list images

docker pull + docker tag + docker push only copies the single-platform image for the runner's architecture (typically linux/amd64). If the staging image is a multi-platform manifest list, the production push will silently downgrade it to a single-arch image. cpln image copy did a registry-to-registry blob copy that preserved manifest lists.

If the app is single-platform only, this is a non-issue in practice — but worth confirming. If multi-arch support is needed, consider docker buildx imagetools create -t "${production_image_ref}" "${source_image_ref}" instead of pull/tag/push, which copies the manifest list without materialising layers on the runner.

Minor – Performance: full image materialisation on runner

The old cpln image copy was a registry-to-registry transfer that never downloaded layers to the CI runner. The new approach pulls the full image to disk, then pushes it. For large images this doubles bandwidth usage and can exhaust runner disk space. Not a blocker if images are small, but worth noting for future growth.

Minor – docker login runs on every retry attempt

Credentials don't change between retries, so re-authenticating on every loop iteration is unnecessary. Moving the two docker login calls above the retry loop would be cleaner and avoids repeated auth round-trips on transient pull/push failures.


Prerequisite

A new CPLN_TOKEN_PRODUCTION secret must be added to the repository before this workflow can succeed — make sure it's provisioned before merging.

Comment on lines +403 to +405
docker pull "${source_image_ref}" &&
docker tag "${source_image_ref}" "${production_image_ref}" &&
docker push "${production_image_ref}"; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multi-arch concern: docker pull + docker tag + docker push only copies the single-platform image matching the runner's architecture. If the staging image is a multi-platform manifest list, production will silently receive a single-arch image.

If multi-arch is not needed, this is fine. Otherwise consider using docker buildx imagetools create, which copies the manifest list without pulling layers:

Suggested change
docker pull "${source_image_ref}" &&
docker tag "${source_image_ref}" "${production_image_ref}" &&
docker push "${production_image_ref}"; then
docker buildx imagetools create -t "${production_image_ref}" "${source_image_ref}"; then

(that replaces the three lines — if this one-liner works in your buildx setup, remove the pull/tag lines entirely).

Comment on lines +398 to +401
if printf '%s' "${CPLN_TOKEN_STAGING}" |
docker login "${staging_registry}" -u '<token>' --password-stdin >/dev/null &&
printf '%s' "${CPLN_TOKEN_PRODUCTION}" |
docker login "${production_registry}" -u '<token>' --password-stdin >/dev/null &&

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two docker login calls run on every retry attempt, but credentials never change between retries. Consider hoisting them above the loop so they run once — retries are really only needed for the pull/tag/push operations.

Suggested change
if printf '%s' "${CPLN_TOKEN_STAGING}" |
docker login "${staging_registry}" -u '<token>' --password-stdin >/dev/null &&
printf '%s' "${CPLN_TOKEN_PRODUCTION}" |
docker login "${production_registry}" -u '<token>' --password-stdin >/dev/null &&
if docker manifest inspect "${source_image_ref}" >/dev/null &&

(Move the two docker login calls to just after the export DOCKER_CONFIG line, before the retry loop.)

@greptile-apps

greptile-apps Bot commented Jun 2, 2026

Copy link
Copy Markdown

Greptile Summary

Replaces the cpln image docker-login + cpln image copy approach with explicit docker login --password-stdin for both staging and production registries, followed by a manual docker pull / docker tag / docker push sequence. This fixes a credential-helper mismatch introduced after PR #757 where the cpln CLI was configuring Docker with the wrong profile for the staging registry.

  • CPLN_TOKEN_PRODUCTION is now passed into the copy step, and both registries are authenticated independently before any image transfer begins.
  • The retry loop now re-authenticates and re-pulls on each attempt, which is correct for transient failures but means a failed mid-copy retry will re-download all layers.
  • docker pull + docker tag + docker push copies only the manifest entry matching the runner's native platform; if the staging image were a multi-arch manifest list, the production copy would be single-arch only.

Confidence Score: 4/5

Safe to merge for single-arch deployments; the new direct docker login approach correctly isolates staging and production credentials and avoids the cpln profile mismatch that broke the previous run.

The credential handling is sound — tokens flow through stdin, the Docker config is isolated to a temp directory that is cleaned up on exit, and both registries are authenticated independently before any transfer begins. The one gap is that docker pull+tag+push does not preserve multi-arch manifest lists, but for this Rails application that is almost certainly not an issue today.

.github/workflows/cpflow-promote-staging-to-production.yml — specifically the retry loop, which re-authenticates and re-downloads the full image on every attempt.

Important Files Changed

Filename Overview
.github/workflows/cpflow-promote-staging-to-production.yml Copy-image step switched from cpln credential-helper to direct docker login for both registries; logic is correct for single-arch images but docker pull+tag+push does not preserve multi-arch manifest lists.

Sequence Diagram

sequenceDiagram
    participant GHA as GitHub Actions Runner
    participant SR as Staging Registry
    participant PR as Production Registry

    loop Retry (up to N attempts)
        GHA->>SR: docker login --password-stdin (CPLN_TOKEN_STAGING)
        SR-->>GHA: Login Succeeded
        GHA->>PR: docker login --password-stdin (CPLN_TOKEN_PRODUCTION)
        PR-->>GHA: Login Succeeded
        GHA->>SR: docker manifest inspect source_image_ref
        SR-->>GHA: Manifest OK
        GHA->>SR: docker pull source_image_ref
        SR-->>GHA: Image layers downloaded
        GHA->>GHA: docker tag source_image_ref production_image_ref
        GHA->>PR: docker push production_image_ref
        PR-->>GHA: Push complete
    end
    GHA->>GHA: "echo image=production_image to GITHUB_OUTPUT"
Loading

Reviews (1): Last reviewed commit: "Copy production image with direct Docker..." | Re-trigger Greptile

Comment on lines +403 to +405
docker pull "${source_image_ref}" &&
docker tag "${source_image_ref}" "${production_image_ref}" &&
docker push "${production_image_ref}"; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 docker pull + push drops multi-arch manifest lists

docker pull fetches only the manifest entry that matches the runner's native platform (e.g., linux/amd64). If the staging image was built as a multi-arch manifest list, the production copy will silently become a single-architecture image. Tools like docker buildx imagetools create or crane copy preserve the full manifest list. This is low-risk for a single-platform Rails app, but worth keeping in mind if the image build ever switches to multi-arch.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@justin808 justin808 merged commit f364714 into master Jun 2, 2026
16 checks passed
@justin808 justin808 deleted the jg-codex/use-direct-docker-copy-for-production branch June 2, 2026 21:46
@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown

✅ Review App Deleted

Review app for PR #758 is deleted

🎮 Control Plane Console
📋 View Workflow Logs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant